/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Threading;
using MyPhotoIndex.Utilities;


namespace MyPhotoIndex.Controls
{
    [Designer(typeof(ScrollbarControlDesigner))]
    public class MyPhotoIndexScrollBar : UserControl
    {
        public new event EventHandler Scroll = null;
        public event EventHandler ValueChanged = null;

        private Image m_downArrowImage_hl = Resources.Resources.ScrollBar_BottomArrow;
        private Image m_upArrowImage_hl = Resources.Resources.ScrollBar_TopArrow;
        private Image m_downArrowImage = Resources.Resources.scrollBar_BottomArrow_no_highlight;
        private Image m_upArrowImage = Resources.Resources.ScrollBar_TopArrow_no_highlight;

        private Image m_thumbTopImage = Resources.Resources.ScrollBar_thumbTop;
        private Image m_thumbSpanImage = Resources.Resources.ScrollBar_thumbSpan;
        private Image m_thumbMiddleImage = Resources.Resources.ScrollBar_thumb;
        private Image m_thumbBottomImage = Resources.Resources.ScrollBar_thumbBottom;

        private Image m_thumbTopImage_hl = Resources.Resources.ScrollBar_thumbTop_hl;
        private Image m_thumbSpanImage_hl = Resources.Resources.ScrollBar_thumbSpan_hl;
        private Image m_thumbMiddleImage_hl = Resources.Resources.ScrollBar_thumb_hl;
        private Image m_thumbBottomImage_hl = Resources.Resources.ScrollBar_thumbBottom_hl;

        NativeScrollBar m_nsb;

        private int m_largeChange = 10;
        private int m_smallChange = 1;
        private int m_minimum = 0;
        private int m_maximum = 100;
        private int m_value = 0;
        private int m_clickPoint;
        private int m_thumbTop = 0;

        private bool m_thumbDown = false;
        private bool m_thumbDragging = false;
        private bool m_mouseOn;
        private bool m_mouseOnThumb;

        private int m_minThumbHeight = 56;
        private int m_trackHeight;
        private float m_thumbHeight;

        public MyPhotoIndexScrollBar()
        {
            InitializeComponent();
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);

            this.Width = 16;
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // CustomScrollbar
            // 
            this.Name = "CustomScrollbar";
            this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseDown);
            this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseMove);
            this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.CustomScrollbar_MouseUp);
            this.MouseEnter += new EventHandler(MyPhotoIndexScrollBar_MouseEnter);
            this.MouseLeave += new EventHandler(MyPhotoIndexScrollBar_MouseLeave);

            Calculate();

            this.ResumeLayout(false);
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);

            if (this.Site == null)
            {
                this.m_nsb = new NativeScrollBar(this);
            }
        }

        protected override void OnHandleDestroyed(EventArgs e)
        {
            base.OnHandleDestroyed(e);

            if (this.m_nsb != null)
            {
                this.m_nsb.DestroyHandle();
            }
        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case Win32Interop.WM_VSCROLL:
                    if ((int)m.WParam == Win32Interop.SB_LINEDOWN)
                    {
                        Value++;
                    }
                    else
                    if ((int)m.WParam == Win32Interop.SB_LINEUP)
                    {
                        Value--;
                    }
                    break;
                case Win32Interop.WM_HSCROLL:
 
                    break;
            }

            base.WndProc(ref m);
        }

        void MyPhotoIndexScrollBar_MouseLeave(object sender, EventArgs e)
        {
            m_mouseOn = false;
            m_mouseOnThumb = false;

            this.Invalidate();
        }

        void MyPhotoIndexScrollBar_MouseEnter(object sender, EventArgs e)
        {
            m_mouseOn = true;

            this.Invalidate();
        }

        private void Calculate()
        {
            m_trackHeight = (this.Height - (m_upArrowImage_hl.Height + m_downArrowImage_hl.Height));
            m_thumbHeight = ((float)m_smallChange / (float)m_maximum) * m_trackHeight;

            if (m_thumbHeight > m_trackHeight)
            {
                m_thumbHeight = m_trackHeight;
            }

            if (m_thumbHeight < m_minThumbHeight)
            {
                m_thumbHeight = m_minThumbHeight;
            }

        }

        protected override void OnSizeChanged(EventArgs e)
        {
            if (this.Height >= 92)
            {
                m_minThumbHeight = 56;
            }
            else
            {
                m_minThumbHeight = 20;
            }

            Calculate();

            base.OnSizeChanged(e);
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("LargeChange")]
        public int LargeChange
        {
            get { return m_largeChange; }
            set
            {
                m_largeChange = value;
                Invalidate();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("SmallChange")]
        public int SmallChange
        {
            get { return m_smallChange; }
            set
            {
                m_smallChange = value;
                Invalidate();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Minimum")]
        public int Minimum
        {
            get { return m_minimum; }
            set
            {
                m_minimum = value;
                Calculate();
                Invalidate();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Maximum")]
        public int Maximum
        {
            get { return m_maximum; }
            set
            {
                m_maximum = value;
                if (m_value >= m_maximum)
                {
                    m_value = m_maximum;
                }

                Calculate();
                Invalidate();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(false), Category("Behavior"), Description("Value")]
        public int Value
        {
            get { return m_value; }
            set
            {
                if (value >= Maximum)
                {
                    value = Maximum - 1;
                }

                if (value < Minimum)
                {
                    value = Minimum;
                }

                m_value = value;

                //figure out value
                int nPixelRange = m_trackHeight - (int)m_thumbHeight;
                int nRealRange = (Maximum - Minimum) - SmallChange;
                float fPerc = 0.0f;
                if (nRealRange != 0)
                {
                    fPerc = (float)m_value / (float)nRealRange;
                }

                float fTop = fPerc * nPixelRange;
                m_thumbTop = (int)fTop;

                Invalidate();
                if (ValueChanged != null)
                {
                    ValueChanged.Invoke(this, EventArgs.Empty);
                }
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;       

            Color firstColor = Color.FromArgb(252, 252, 252);
            Color secondColor = Color.FromArgb(235, 237, 239);
            Rectangle drawRect = new Rectangle(1, m_upArrowImage_hl.Height, this.Width - 2, (this.Height - m_downArrowImage_hl.Height));

            using (Brush brush = new LinearGradientBrush(drawRect, firstColor, secondColor, LinearGradientMode.Horizontal))
            {
                e.Graphics.FillRectangle(brush, new Rectangle(1, m_upArrowImage_hl.Height, this.Width - 2, (this.Height - m_downArrowImage_hl.Height)));
            }

            //draw channel left and right border colors
            using (Brush whiteBrush = new SolidBrush(Color.FromArgb(255, 255, 255)))
            {
                e.Graphics.FillRectangle(whiteBrush, new Rectangle(0, 0, 1, (this.Height - m_downArrowImage_hl.Height)));
                e.Graphics.FillRectangle(whiteBrush, new Rectangle(this.Width - 1, 0, 1, (this.Height - m_downArrowImage_hl.Height)));
            }

            e.Graphics.DrawImageUnscaled(m_mouseOn ? m_upArrowImage_hl : m_upArrowImage, new Point(1, 1));
            e.Graphics.DrawImageUnscaled(m_mouseOn ? m_downArrowImage_hl : m_downArrowImage, new Point(1, (this.Height - m_downArrowImage_hl.Height)));

            if (this.Enabled == false)
            {
                return;
            }

            //draw thumb
            float fSpanHeight = (m_thumbHeight - (m_thumbMiddleImage.Height + m_thumbTopImage.Height + m_thumbBottomImage.Height)) / 2.0f;
            int nSpanHeight = (int)fSpanHeight;

            int nTop = m_thumbTop;
            nTop += m_upArrowImage_hl.Height;

            //draw top
            e.Graphics.DrawImageUnscaled(m_mouseOnThumb ? m_thumbTopImage_hl : m_thumbTopImage, new Point(1, nTop));

            nTop += m_thumbTopImage.Height;
            //draw top span
            Rectangle rect = new Rectangle(1, nTop, this.Width - 2, nSpanHeight);
            e.Graphics.DrawImage(m_mouseOnThumb ? m_thumbSpanImage_hl : m_thumbSpanImage, 1.0f, (float)nTop, (float)this.Width - 2.0f, (float)fSpanHeight * 2);

            nTop += nSpanHeight;
            //draw middle
            e.Graphics.DrawImageUnscaled(m_mouseOnThumb ? m_thumbMiddleImage_hl : m_thumbMiddleImage, new Point(1, nTop));

            nTop += m_thumbMiddleImage.Height;
            //draw top span
            rect = new Rectangle(1, nTop, this.Width - 2, nSpanHeight * 2);
            e.Graphics.DrawImage(m_mouseOnThumb ? m_thumbSpanImage_hl : m_thumbSpanImage, rect);

            nTop += nSpanHeight;
            //draw bottom
            e.Graphics.DrawImageUnscaled(m_mouseOnThumb ? m_thumbBottomImage_hl : m_thumbBottomImage, new Point(1, nTop)); 
        }

        public override bool AutoSize
        {
            get
            {
                return base.AutoSize;
            }
            set
            {
                base.AutoSize = value;
                if (base.AutoSize)
                {
                    this.Width = m_upArrowImage_hl.Width;
                }
            }
        }

        private void CustomScrollbar_MouseDown(object sender, MouseEventArgs e)
        {
            Point ptPoint = this.PointToClient(Cursor.Position);

            int nTop = m_thumbTop;
            nTop += m_upArrowImage_hl.Height;

            Rectangle thumbrect = new Rectangle(new Point(1, nTop), new Size(m_thumbMiddleImage.Width, (int)m_thumbHeight));
            if (thumbrect.Contains(ptPoint))
            {
                //hit the thumb
                m_clickPoint = (ptPoint.Y - nTop); ;
                this.m_thumbDown = true;
                return;
            }

            Rectangle uparrowrect = new Rectangle(new Point(1, 0), new Size(m_upArrowImage_hl.Width, m_upArrowImage_hl.Height));
            if (uparrowrect.Contains(ptPoint))
            {
                while (MouseButtons == MouseButtons.Left)
                {
                    if (m_value - SmallChange >= Minimum)
                    {
                        Value = m_value - SmallChange;
                    }

                    Thread.Sleep(10);
                    Application.DoEvents();
                }

                return;
            }

            Rectangle downarrowrect = new Rectangle(new Point(1, m_upArrowImage_hl.Height + m_trackHeight), new Size(m_upArrowImage_hl.Width, m_upArrowImage_hl.Height));
            if (downarrowrect.Contains(ptPoint))
            {
                while (MouseButtons == MouseButtons.Left)
                {
                    if (m_value + SmallChange <= Maximum)
                    {
                        Value = m_value + SmallChange;
                    }

                    Thread.Sleep(10);
                    Application.DoEvents();
                }

                return;
            }

            bool up = (ptPoint.Y < nTop);
            while (MouseButtons == MouseButtons.Left)
            {
                if (m_value + LargeChange <= Maximum)
                {
                    Value = m_value + (LargeChange * (up ? -1 : 1));
                }
                else
                    if (m_value + SmallChange <= Maximum)
                    {
                        Value = m_value + (SmallChange * (up ? -1 : 1));
                    }

                Thread.Sleep(10);
                Application.DoEvents();
            }
        }

        private void CustomScrollbar_MouseUp(object sender, MouseEventArgs e)
        {
            this.m_thumbDown = false;
            this.m_thumbDragging = false;
        }

        private void MoveThumb(int y)
        {
            int nRealRange = Maximum - Minimum;
            int nSpot = m_clickPoint;

            int nPixelRange = (m_trackHeight - (int)m_thumbHeight);
            if (m_thumbDown && nRealRange > 0)
            {
                if (nPixelRange > 0)
                {
                    int nNewThumbTop = y - (m_upArrowImage_hl.Height + nSpot);

                    if (nNewThumbTop < 0)
                    {
                        m_thumbTop = nNewThumbTop = 0;
                    }
                    else if (nNewThumbTop > nPixelRange)
                    {
                        m_thumbTop = nNewThumbTop = nPixelRange;
                    }
                    else
                    {
                        m_thumbTop = y - (m_upArrowImage_hl.Height + nSpot);
                    }

                    //figure out value
                    float fPerc = (float)m_thumbTop / (float)nPixelRange;
                    float fValue = fPerc * (Maximum - SmallChange);
                    m_value = (int)fValue;
                    Debug.WriteLine(m_value.ToString());

                    Application.DoEvents();

                    Invalidate();
                }
            }
        }

        private void CustomScrollbar_MouseMove(object sender, MouseEventArgs e)
        {
            if (m_thumbDown == true)
            {
                this.m_thumbDragging = true;
            }

            if (this.m_thumbDragging)
            {
                int value = m_value;

                MoveThumb(e.Y);

                if (ValueChanged != null &&
                    value != m_value)
                {
                    ValueChanged(this, EventArgs.Empty);
                }

                if (Scroll != null)
                {
                    Scroll(this, EventArgs.Empty);
                }
            }

            int thumbTop = m_thumbTop + m_upArrowImage_hl.Height;
            Rectangle thumbrect = new Rectangle(new Point(1, thumbTop), new Size(m_thumbMiddleImage.Width, (int)m_thumbHeight));
            if (m_mouseOn)
            {
                if (thumbrect.Contains(this.PointToClient(Cursor.Position)))
                {
                    if (m_mouseOnThumb == false)
                    {
                        m_mouseOnThumb = true;
                        this.Invalidate();
                    }
                }
                else
                {
                    if (m_mouseOnThumb == true)
                    {
                        m_mouseOnThumb = false;
                        this.Invalidate();
                    }
                }
            }
        }

    }

    internal class ScrollbarControlDesigner : System.Windows.Forms.Design.ControlDesigner
    {
        public override SelectionRules SelectionRules
        {
            get
            {
                SelectionRules selectionRules = base.SelectionRules;
                PropertyDescriptor propDescriptor = TypeDescriptor.GetProperties(this.Component)["AutoSize"];
                if (propDescriptor != null)
                {
                    bool autoSize = (bool)propDescriptor.GetValue(this.Component);
                    if (autoSize)
                    {
                        selectionRules = SelectionRules.Visible | SelectionRules.Moveable | SelectionRules.BottomSizeable | SelectionRules.TopSizeable;
                    }
                    else
                    {
                        selectionRules = SelectionRules.Visible | SelectionRules.AllSizeable | SelectionRules.Moveable;
                    }
                }
                return selectionRules;
            }
        }
    }
}